{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: torch==2.2 in /opt/conda/lib/python3.10/site-packages (2.2.0)\n",
      "Requirement already satisfied: filelock in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (3.13.1)\n",
      "Requirement already satisfied: typing-extensions>=4.8.0 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (4.9.0)\n",
      "Requirement already satisfied: sympy in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (1.12)\n",
      "Requirement already satisfied: networkx in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (2.8.5)\n",
      "Requirement already satisfied: jinja2 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (3.1.2)\n",
      "Requirement already satisfied: fsspec in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (2023.12.2)\n",
      "Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.105)\n",
      "Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (8.9.2.26)\n",
      "Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.3.1)\n",
      "Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (11.0.2.54)\n",
      "Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (10.3.2.106)\n",
      "Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (11.4.5.107)\n",
      "Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.0.106)\n",
      "Requirement already satisfied: nvidia-nccl-cu12==2.19.3 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (2.19.3)\n",
      "Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (12.1.105)\n",
      "Requirement already satisfied: triton==2.2.0 in /opt/conda/lib/python3.10/site-packages (from torch==2.2) (2.2.0)\n",
      "Requirement already satisfied: nvidia-nvjitlink-cu12 in /opt/conda/lib/python3.10/site-packages (from nvidia-cusolver-cu12==11.4.5.107->torch==2.2) (12.3.101)\n",
      "Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.10/site-packages (from jinja2->torch==2.2) (2.1.3)\n",
      "Requirement already satisfied: mpmath>=0.19 in /opt/conda/lib/python3.10/site-packages (from sympy->torch==2.2) (1.3.0)\n",
      "Requirement already satisfied: matplotlib==3.5.2 in /opt/conda/lib/python3.10/site-packages (3.5.2)\n",
      "Requirement already satisfied: cycler>=0.10 in /opt/conda/lib/python3.10/site-packages (from matplotlib==3.5.2) (0.12.1)\n",
      "Requirement already satisfied: fonttools>=4.22.0 in /opt/conda/lib/python3.10/site-packages (from matplotlib==3.5.2) (4.46.0)\n",
      "Requirement already satisfied: kiwisolver>=1.0.1 in /opt/conda/lib/python3.10/site-packages (from matplotlib==3.5.2) (1.4.5)\n",
      "Requirement already satisfied: numpy>=1.17 in /opt/conda/lib/python3.10/site-packages (from matplotlib==3.5.2) (1.26.2)\n",
      "Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.10/site-packages (from matplotlib==3.5.2) (21.3)\n",
      "Requirement already satisfied: pillow>=6.2.0 in /opt/conda/lib/python3.10/site-packages (from matplotlib==3.5.2) (10.2.0)\n",
      "Requirement already satisfied: pyparsing>=2.2.1 in /opt/conda/lib/python3.10/site-packages (from matplotlib==3.5.2) (3.0.9)\n",
      "Requirement already satisfied: python-dateutil>=2.7 in /opt/conda/lib/python3.10/site-packages (from matplotlib==3.5.2) (2.8.2)\n",
      "Requirement already satisfied: six>=1.5 in /opt/conda/lib/python3.10/site-packages (from python-dateutil>=2.7->matplotlib==3.5.2) (1.16.0)\n",
      "Requirement already satisfied: scikit-image==0.19.3 in /opt/conda/lib/python3.10/site-packages (0.19.3)\n",
      "Requirement already satisfied: numpy>=1.17.0 in /opt/conda/lib/python3.10/site-packages (from scikit-image==0.19.3) (1.26.2)\n",
      "Requirement already satisfied: scipy>=1.4.1 in /opt/conda/lib/python3.10/site-packages (from scikit-image==0.19.3) (1.11.4)\n",
      "Requirement already satisfied: networkx>=2.2 in /opt/conda/lib/python3.10/site-packages (from scikit-image==0.19.3) (2.8.5)\n",
      "Requirement already satisfied: pillow!=7.1.0,!=7.1.1,!=8.3.0,>=6.1.0 in /opt/conda/lib/python3.10/site-packages (from scikit-image==0.19.3) (10.2.0)\n",
      "Requirement already satisfied: imageio>=2.4.1 in /opt/conda/lib/python3.10/site-packages (from scikit-image==0.19.3) (2.33.0)\n",
      "Requirement already satisfied: tifffile>=2019.7.26 in /opt/conda/lib/python3.10/site-packages (from scikit-image==0.19.3) (2023.12.9)\n",
      "Requirement already satisfied: PyWavelets>=1.1.1 in /opt/conda/lib/python3.10/site-packages (from scikit-image==0.19.3) (1.5.0)\n",
      "Requirement already satisfied: packaging>=20.0 in /opt/conda/lib/python3.10/site-packages (from scikit-image==0.19.3) (21.3)\n",
      "Requirement already satisfied: pyparsing!=3.0.5,>=2.0.2 in /opt/conda/lib/python3.10/site-packages (from packaging>=20.0->scikit-image==0.19.3) (3.0.9)\n"
     ]
    }
   ],
   "source": [
    "!pip install torch==2.2\n",
    "!pip install matplotlib==3.5.2\n",
    "!pip install scikit-image==0.19.3"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-27T12:20:43.948987Z",
     "start_time": "2018-11-27T12:20:30.225783Z"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "import numpy as np\n",
    "import random\n",
    "import skimage.io as io\n",
    "from struct import pack, unpack\n",
    "from io import StringIO, BytesIO\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "from torch.autograd import Variable\n",
    "import torch.utils.data as data"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Defining MIDI constants"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "NOTE_MIDI_OFF = 0x80\n",
    "NOTE_MIDI_ON = 0x90\n",
    "A_TOUCH = 0xA0\n",
    "CONT_CONTROL = 0xB0 \n",
    "PATCH_CHG_MIDI = 0xC0\n",
    "CHNL_PRESS = 0xD0\n",
    "MIDI_PITCH_BND = 0xE0                                                                                  \n",
    "SYS_EXCL_MIDI = 0xF0\n",
    "MTC_MIDI = 0xF1\n",
    "SNG_POS_MIDI_POINTER = 0xF2\n",
    "SNG_SEL_MIDI = 0xF3\n",
    "TUNE_REQ_MIDI = 0xF6\n",
    "END_OFF_EXCL_MIDI = 0xF7 \n",
    "SEQ_NUM = 0x00  \n",
    "MIDI_TXT            = 0x01  \n",
    "MIDI_CR       = 0x02  \n",
    "SEQ_NAME_MIDI   = 0x03  \n",
    "INSTR_NAME_MIDI = 0x04  \n",
    "MIDI_LRC           = 0x05  \n",
    "MIDI_MRKR          = 0x06  \n",
    "CUEPNT_MIDI        = 0x07  \n",
    "PROG_NAME_MIDI    = 0x08  \n",
    "DVC_NAME     = 0x09  \n",
    "CH_PREF  = 0x20  \n",
    "PRT       = 0x21  \n",
    "E_O_T    = 0x2F  \n",
    "TMP           = 0x51  \n",
    "SMTP_MIDI_OFF     = 0x54  \n",
    "TIME_SIGN_MIDI  = 0x58  \n",
    "KEY_SIGN_MIDI   = 0x59  \n",
    "MIDI_SPEC        = 0x7F  \n",
    "MIDI_TRK_HEAD    = 'MTrk'\n",
    "MIDI_MET_EVNT     = 0xFF"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## MIDI handling code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MOStrm:\n",
    "    def __init__(self):\n",
    "        self._abs_t = 0\n",
    "        self._rel_t = 0\n",
    "        self._cur_trk = 0\n",
    "        self._run_stat = None\n",
    "    def time_update(self, t_new=0, rel_flag=1):\n",
    "        if rel_flag:\n",
    "            self._rel_t = t_new\n",
    "            self._abs_t += t_new\n",
    "        else:\n",
    "            self._rel_t = t_new - self._abs_t\n",
    "            self._abs_t = t_new\n",
    "    def t_reset(self):\n",
    "        self._rel_t = 0\n",
    "        self._abs_t = 0        \n",
    "    def t_rel(self):\n",
    "        return self._rel_t\n",
    "    def t_abs(self):\n",
    "        return self._abs_t    \n",
    "    def set_cur_trk(self, trk_new):\n",
    "        self._cur_trk = trk_new\n",
    "    def midi_nt_on(self, chnl=0, nt=0x40, vel=0x40):\n",
    "        pass\n",
    "    def midi_nt_off(self, chnl=0, nt=0x40, vel=0x40):\n",
    "        pass\n",
    "    def aft_tch(self, chnl=0, nt=0x40, vel=0x40):\n",
    "        pass\n",
    "    def cont_ctrl(self, chnl, crtler, v):\n",
    "        pass\n",
    "    def pch_chg(self, chnl, pch):\n",
    "        pass\n",
    "    def chnl_press(self, chnl, press):\n",
    "        pass\n",
    "    def pch_bnd(self, chnl, v):\n",
    "        pass\n",
    "    def sng_pos_ptr(self, v):\n",
    "        pass\n",
    "    def sng_sel(self, sng_num):\n",
    "        pass\n",
    "    def tun_req(self):\n",
    "        pass        \n",
    "    def t_cod(self, typ_m, vals):\n",
    "        pass\n",
    "    def hdr(self, fmt=0, num_trs=1, divs=96):\n",
    "        pass\n",
    "    def end_of_file(self):\n",
    "        pass\n",
    "    def mt_evt(self, mt_typ, d):\n",
    "        pass\n",
    "    def s_o_trk(self, num_trk=0):\n",
    "        pass\n",
    "    def e_o_trk(self):\n",
    "        pass\n",
    "    def seq_num(self, v):\n",
    "        pass\n",
    "    def txt(self, txt):\n",
    "        pass\n",
    "    def cr(self, txt):\n",
    "        pass\n",
    "    def seq_nam(self, txt):\n",
    "        pass\n",
    "    def inst_nam(self, txt):\n",
    "        pass\n",
    "    def lrc(self, txt):\n",
    "        pass\n",
    "    def mrkr(self, txt):\n",
    "        pass\n",
    "    def cuepnt(self, txt):\n",
    "        pass\n",
    "    def ch_prf(self, chnl):\n",
    "        pass\n",
    "    def m_prt(self, v):\n",
    "        pass\n",
    "    def tmp(self, v):\n",
    "        pass\n",
    "    def smtp_midi_off(self, hr, mn, sec, fm, fmprt):\n",
    "        pass\n",
    "    def t_sign_midi(self, n, d, c, b):\n",
    "        pass\n",
    "    def k_sign_midi(self, s, m):\n",
    "        pass\n",
    "    def seq_spc(self, d):\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MIFl:\n",
    "    def __init__(self, ostr, ifl):\n",
    "        self.r_input = RIStrFl(ifl)\n",
    "        self.prs = MFlPrsr(self.r_input, ostr)\n",
    "    def op_rd(self):\n",
    "        prs = self.prs\n",
    "        prs.pMdChk()\n",
    "        prs.pMTChks()\n",
    "    def d_set(self, d=''):\n",
    "        self.r_input.d_set(d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MOFl(MOStrm):\n",
    "    def __init__(self, r_output=''):\n",
    "        self.r_output = ROStrFl(r_output)\n",
    "        MOStrm.__init__(self)    \n",
    "    def op_wrt(self):\n",
    "        self.r_output.op_wrt()\n",
    "    def midi_evt_slc(self, e_slice):\n",
    "        curr_track = self._cur_trk_bfr\n",
    "        curr_track.op_wrt_var_len(self.t_rel())\n",
    "        curr_track.op_wrt_slc(e_slice)\n",
    "    def midi_nt_on(self, chnl=0, nt=0x40, vel=0x40):\n",
    "        curr_slice = strm_to_data([NOTE_MIDI_ON + chnl, nt, vel])\n",
    "        self.midi_evt_slc(curr_slice)\n",
    "    def midi_nt_off(self, chnl=0, nt=0x40, vel=0x40):\n",
    "        curr_slice = strm_to_data([NOTE_MIDI_OFF + chnl, nt, vel])\n",
    "        self.midi_evt_slc(curr_slice)\n",
    "    def aft_tch(self, chnl=0, nt=0x40, vel=0x40):\n",
    "        curr_slice = strm_to_data([A_TOUCH + chnl, nt, vel])\n",
    "        self.midi_evt_slc(curr_slice)\n",
    "    def cont_ctrl(self, chnl, ctrler, v):\n",
    "        curr_slice = strm_to_data([CONT_CONTROL + chnl, ctrler, v])\n",
    "        self.midi_evt_slc(curr_slice)\n",
    "    def pch_chg(self, chnl, pch):\n",
    "        curr_slice = strm_to_data([PATCH_CHG_MIDI + chnl, pch])\n",
    "        self.midi_evt_slc(curr_slice)\n",
    "    def chnl_press(self, chnl, press):\n",
    "        curr_slice = strm_to_data([CHNL_PRESS + chnl, press])\n",
    "        self.midi_evt_slc(curr_slice)\n",
    "    def pch_bnd(self, chnl, v):\n",
    "        m = (v>>7) & 0xFF\n",
    "        l = v & 0xFF\n",
    "        curr_slice = strm_to_data([MIDI_PITCH_BND + chnl, m, l])\n",
    "        self.midi_evt_slc(curr_slice)\n",
    "    def t_cod(self, typ_m, vals):\n",
    "        v = (typ_m<<4) + vals\n",
    "        self.midi_evt_slc(strm_to_data([MIDI_TIME_CODE, v]))\n",
    "    def sng_pos_ptr(self, v):\n",
    "        l = (v & 0x7F)\n",
    "        m = (v >> 7) & 0x7F\n",
    "        self.midi_evt_slc(strm_to_data([SNG_POS_MIDI_POINTER, l, m]))\n",
    "    def sng_sel(self, sng_num):\n",
    "        self.midi_evt_slc(strm_to_data([SNG_SEL_MIDI, sng_num]))\n",
    "    def tun_req(self):\n",
    "        self.midi_evt_slc(chr(TUNE_REQ_MIDI))\n",
    "    def hdr(self, fmt=0, num_trs=1, divs=96):       \n",
    "        r_data = self.r_output\n",
    "        r_data.op_wrt_slc('MThd')\n",
    "        b = r_data.op_bew_wrt\n",
    "        b(6, 4)\n",
    "        b(fmt, 2)\n",
    "        b(num_trs, 2)\n",
    "        b(divs, 2)\n",
    "    def end_of_file(self):\n",
    "        self.op_wrt()\n",
    "    def m_slc(self, mt_typ, data_slice):\n",
    "        slc = strm_to_data([MIDI_MET_EVNT, mt_typ]) + \\\n",
    "                         op_wrt_var(len(data_slice)) +  data_slice\n",
    "        self.midi_evt_slc(slc)\n",
    "    def mt_evt(self, mt_typ, d):\n",
    "        self.m_slc(mt_typ, strm_to_data(d))\n",
    "    def s_o_trk(self, num_trk=0):\n",
    "        self._cur_trk_bfr = ROStrFl()\n",
    "        self.t_reset()\n",
    "        self._cur_trk += 1\n",
    "    def e_o_trk(self):\n",
    "        r_output = self.r_output\n",
    "        r_output.op_wrt_slc(MIDI_TRK_HEAD)\n",
    "        d_trk = self._cur_trk_bfr.fetch_val()\n",
    "        e_o_t_slc = op_wrt_var(self.t_rel()) + strm_to_data([MIDI_MET_EVNT, E_O_T, 0])\n",
    "        r_output.op_bew_wrt(len(d_trk)+len(e_o_t_slc), 4)\n",
    "        r_output.op_wrt_slc(d_trk)\n",
    "        r_output.op_wrt_slc(e_o_t_slc)\n",
    "    def seq_num(self, v):\n",
    "        self.m_slc(mt_typ, op_bew_wrt(v, 2))\n",
    "    def midi_txt(self, txt):\n",
    "        self.m_slc(MIDI_TXT, txt)\n",
    "    def cr(self, txt):\n",
    "        self.m_slc(MIDI_CR, txt)\n",
    "    def seq_nam(self, txt):\n",
    "        self.m_slc(SEQ_NAME_MIDI, txt)\n",
    "    def inst_nam(self, txt):\n",
    "        self.m_slc(INSTR_NAME_MIDI, txt)\n",
    "    def lrc(self, txt):\n",
    "        self.m_slc(MIDI_LRC, txt)\n",
    "    def mrkr(self, txt):\n",
    "        self.m_slc(MIDI_MRKR, txt)\n",
    "    def cuepnt(self, txt):\n",
    "        self.m_slc(CUEPNT_MIDI, txt)\n",
    "    def ch_prf(self, chnl):\n",
    "        self.m_slc(CH_PREF, chr(chnl))\n",
    "    def m_prt(self, v):\n",
    "        self.m_slc(CH_PREF, chr(v))\n",
    "    def tmp(self, v):\n",
    "        h, m, l = (v>>16 & 0xff), (v>>8 & 0xff), (v & 0xff)\n",
    "        self.m_slc(TMP, strm_to_data([h, m, l]))\n",
    "    def smtp_midi_off(self, hr, mn, sec, fm, fmprt):\n",
    "        self.m_slc(SMTP_MIDI_OFF, strm_to_data([hr, mn, sec, fm, fmprt]))\n",
    "    def t_sign_midi(self, n, d, c, b):\n",
    "        self.m_slc(TIME_SIGN_MIDI, strm_to_data([n, d, c, b]))\n",
    "    def k_sign_midi(self, s, m):\n",
    "        self.m_slc(KEY_SIGN_MIDI, strm_to_data([s, m]))\n",
    "    def seq_spc(self, d):\n",
    "        self.m_slc(SEQ_SPC, d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "class RIStrFl:    \n",
    "    def __init__(self, ifl=''):\n",
    "        if ifl:\n",
    "            if isinstance(ifl, str):\n",
    "                ifl = open(ifl, 'rb')\n",
    "                self.d = ifl.read()\n",
    "                ifl.close()\n",
    "            else:\n",
    "                self.d = ifl.op_rd()\n",
    "        else:\n",
    "            self.d = ''\n",
    "        self.csr = 0\n",
    "    def d_set(self, d=''):\n",
    "        self.d = d\n",
    "    def get_csr(self):\n",
    "        return self.csr\n",
    "    def mv_csr(self, rl_ps=0):\n",
    "        self.csr += rl_ps\n",
    "    def nxt_slc(self, ln, mv_csr=1):\n",
    "        c = self.csr\n",
    "        slc = self.d[int(c):int(c+ln)]\n",
    "        if mv_csr:\n",
    "            self.mv_csr(ln)\n",
    "        return slc\n",
    "    def b_read(self, num_bts=1, mv_csr=1):\n",
    "        return b_read(self.nxt_slc(num_bts, mv_csr))\n",
    "    def op_rd_var_len(self):\n",
    "        MX_VLN = 4\n",
    "        v = op_rd_var(self.nxt_slc(MX_VLN, 0))\n",
    "        self.mv_csr(get_len_var(v))\n",
    "        return v"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "class ROStrFl:\n",
    "    def __init__(self, ofl=''):\n",
    "        self.bfr = BytesIO()\n",
    "        self.ofl = ofl\n",
    "    def op_wrt_slc(self, s_slc):\n",
    "        if isinstance(s_slc, str):\n",
    "            self.bfr.write(s_slc.encode())\n",
    "        else:\n",
    "            self.bfr.write(s_slc)\n",
    "    def op_bew_wrt(self, v, ln=1):\n",
    "        self.op_wrt_slc(op_bew_wrt(v, ln))\n",
    "    def op_wrt_var_len(self, v):\n",
    "        var = self.op_wrt_slc(op_wrt_var(v))\n",
    "    def op_wrt(self):\n",
    "        if self.ofl:\n",
    "            if isinstance(self.ofl, str):\n",
    "                ofl = open(self.ofl, 'wb')\n",
    "                ofl.write(self.fetch_val())\n",
    "                ofl.close()\n",
    "            else:\n",
    "                self.ofl.op_wrt(self.fetch_val())\n",
    "        else:\n",
    "            sys.stdout.op_wrt(self.fetch_val())                \n",
    "    def fetch_val(self):\n",
    "        return self.bfr.getvalue()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MFlPrsr:\n",
    "    def __init__(self, r_input, ostr):\n",
    "        self.r_input = r_input\n",
    "        self.dpch = EvtDspch(ostr)\n",
    "        self._run_stat = None\n",
    "    def pMdChk(self):\n",
    "        r_input = self.r_input\n",
    "        hd_chk_typ = r_input.nxt_slc(4)\n",
    "        hd_chk_sz = r_input.b_read(4)\n",
    "        if hd_chk_typ != b'MThd':\n",
    "            raise TypeError(\"Invalid file type - non MIDI !\")\n",
    "        self.fmt = r_input.b_read(2)\n",
    "        self.num_tr = r_input.b_read(2)\n",
    "        self.divs = r_input.b_read(2)\n",
    "        if hd_chk_sz > 6:\n",
    "            r_input.mv_csr(hd_chk_sz-6)\n",
    "        self.dpch.hdr(self.fmt, self.num_tr, self.divs)\n",
    "    def pMTChk(self):\n",
    "        self.dpch.t_reset()        \n",
    "        dpch = self.dpch\n",
    "        r_input = self.r_input\n",
    "        dpch.s_o_trk(self._cur_trk)\n",
    "        r_input.mv_csr(4)\n",
    "        tr_len = r_input.b_read(4)\n",
    "        tr_end = r_input.get_csr() + tr_len\n",
    "        while r_input.get_csr() < tr_end:\n",
    "            t = r_input.op_rd_var_len()\n",
    "            dpch.time_update(t)\n",
    "            pk_fwd = r_input.b_read(mv_csr=0)\n",
    "            if (pk_fwd & 0x80): \n",
    "                st = self._run_stat = r_input.b_read()\n",
    "            else:\n",
    "                st = self._run_stat\n",
    "            h_n, l_n = st & 0xF0, st & 0x0F\n",
    "            if st == MIDI_MET_EVNT:\n",
    "                mt_typ = r_input.b_read()\n",
    "                mt_ln = r_input.op_rd_var_len()\n",
    "                mt_d = r_input.nxt_slc(mt_ln)\n",
    "                dpch.mt_evt(mt_typ, mt_d)\n",
    "            elif st == SYS_EXCL_MIDI:\n",
    "                ssx_ln = r_input.op_rd_var_len()\n",
    "                ssx_d = r_input.nxt_slc(ssx_ln-1)\n",
    "                if r_input.b_read(mv_csr=0) == END_OFF_EXCL_MIDI:\n",
    "                    e_o_ssx = r_input.b_read()\n",
    "                dpch.ssx_ev(ssx_d)\n",
    "            elif h_n == 0xF0:\n",
    "                d_sz = {\n",
    "                    MTC_MIDI:1,\n",
    "                    SNG_POS_MIDI_POINTER:2,\n",
    "                    SNG_SEL_MIDI:1,\n",
    "                }\n",
    "                d_s = d_sz.get(h_n, 0)\n",
    "                cmn_d = r_input.nxt_slc(d_s)\n",
    "                cmn_t = l_n\n",
    "                dpch.sys_cmn(cmn_t, cmn_d)\n",
    "            else:\n",
    "                d_sz = {\n",
    "                    PATCH_CHG_MIDI:1,\n",
    "                    CHNL_PRESS:1,\n",
    "                    NOTE_MIDI_OFF:2,\n",
    "                    NOTE_MIDI_ON:2,\n",
    "                    A_TOUCH:2,\n",
    "                    CONT_CONTROL:2,\n",
    "                    MIDI_PITCH_BND:2,\n",
    "                }\n",
    "                d_s = d_sz.get(h_n, 0)\n",
    "                ch_d = r_input.nxt_slc(d_s)\n",
    "                ev_ty, chnl = h_n, l_n\n",
    "                dpch.chnl_msg(ev_ty, chnl, ch_d)\n",
    "    def pMTChks(self):\n",
    "        for t in range(self.num_tr):\n",
    "            self._cur_trk = t\n",
    "            self.pMTChk()\n",
    "        self.dpch.end_of_file()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class EvtDspch:\n",
    "    def __init__(self, ostr):\n",
    "        self.ostr = ostr\n",
    "        self.conv_vel_zero = 1\n",
    "        self.cont_ctrl_dpch = 1\n",
    "        self.dpch = 1\n",
    "    def hdr(self, fmt, num_trs, divs):\n",
    "        self.ostr.hdr(fmt, num_trs, divs)\n",
    "    def s_o_trk(self, cur_trk):\n",
    "        self.ostr.set_cur_trk(cur_trk)\n",
    "        self.ostr.s_o_trk(cur_trk)\n",
    "    def ssx_ev(self, d):\n",
    "        self.ostr.ssx_ev(d)\n",
    "    def end_of_file(self):\n",
    "        self.ostr.end_of_file()\n",
    "    def time_update(self, t_new=0, rel_flag=1):\n",
    "        self.ostr.time_update(t_new, rel_flag)\n",
    "    def t_reset(self):\n",
    "        self.ostr.t_reset()\n",
    "    def chnl_msg(self, h_n, chnl, d):\n",
    "        strm = self.ostr\n",
    "        d = data_to_strm(d)\n",
    "        if (NOTE_MIDI_ON & 0xF0) == h_n:\n",
    "            nt, vel = d\n",
    "            if vel==0 and self.conv_vel_zero:\n",
    "                strm.midi_nt_off(chnl, nt, 0x40)\n",
    "            else:\n",
    "                strm.midi_nt_on(chnl, nt, vel)        \n",
    "        elif (NOTE_MIDI_OFF & 0xF0) == h_n:\n",
    "            nt, vel = d\n",
    "            strm.midi_nt_off(chnl, nt, vel)        \n",
    "        elif (A_TOUCH & 0xF0) == h_n:\n",
    "            nt, vel = d\n",
    "            strm.aft_tch(chnl, nt, vel)        \n",
    "        elif (CONT_CONTROL & 0xF0) == h_n:\n",
    "            ctrlr, v = d\n",
    "            if self.cont_ctrl_dpch:\n",
    "                self.cnt_ctrls(chnl, ctrlr, v)\n",
    "            else:\n",
    "                strm.cont_ctrl(chnl, ctrlr, v)\n",
    "        elif (PATCH_CHG_MIDI & 0xF0) == h_n:\n",
    "            prg = d[0]\n",
    "            strm.pch_chg(chnl, prg)    \n",
    "        elif (CHNL_PRESS & 0xF0) == h_n:\n",
    "            press = d[0]\n",
    "#             strm.ch_press(chnl, press)            \n",
    "        elif (MIDI_PITCH_BND & 0xF0) == h_n:\n",
    "            hb, lb = d\n",
    "            v = (hb<<7) + lb\n",
    "            strm.pch_bnd(chnl, v)\n",
    "        else:\n",
    "            raise ValueError('Channel message error - illegal message !')            \n",
    "    def cnt_ctrls(self, chnl, ctrlr, v):\n",
    "        strm = self.ostr\n",
    "        strm.cont_ctrl(chnl, ctrlr, v)\n",
    "    def mt_evt(self, mt_typ, d):\n",
    "        strm = self.ostr\n",
    "        if mt_typ == SEQ_NUM:\n",
    "            n = b_read(d)\n",
    "            strm.seq_num(n)\n",
    "        elif mt_typ == MIDI_TXT:\n",
    "            strm.txt(d)\n",
    "        elif mt_typ == MIDI_CR:\n",
    "            strm.cr(d)\n",
    "        elif mt_typ == SEQ_NAME_MIDI:\n",
    "            strm.seq_nam(d)\n",
    "        elif mt_typ == INSTR_NAME_MIDI:\n",
    "            strm.inst_nam(d)\n",
    "        elif mt_typ == MIDI_LRC:\n",
    "            strm.lrc(d)\n",
    "        elif mt_typ == MIDI_MRKR:\n",
    "            strm.mrkr(d)\n",
    "        elif mt_typ == CUEPNT_MIDI:\n",
    "            strm.cuepnt(d)\n",
    "        elif mt_typ == DVC_NAME:\n",
    "            strm.dvc_nam(d)\n",
    "        elif mt_typ == CH_PREF:\n",
    "            chnl = b_read(d)\n",
    "            strm.ch_prf(chnl)\n",
    "        elif mt_typ == PRT:\n",
    "            port = b_read(d)\n",
    "            strm.m_prt(port)\n",
    "        elif mt_typ == E_O_T:\n",
    "            strm.e_o_trk()\n",
    "        elif mt_typ == TMP:\n",
    "            b1, b2, b3 = data_to_strm(d)\n",
    "            strm.tmp((b1<<16) + (b2<<8) + b3)\n",
    "        elif mt_typ == SMTP_MIDI_OFF:\n",
    "            hr, mn, sec, fm, fmprt = data_to_strm(d)\n",
    "            strm.smtp_midi_off(\n",
    "                    hr, mn, sec, fm, fmprt)\n",
    "        elif mt_typ == TIME_SIGN_MIDI:\n",
    "            n, d, c, b = data_to_strm(d)\n",
    "            strm.t_sign_midi(n, d, c, b)\n",
    "        elif mt_typ == KEY_SIGN_MIDI:\n",
    "            sf, mi = data_to_strm(d)\n",
    "            strm.k_sign_midi(sf, mi)\n",
    "        elif mt_typ == MIDI_SPEC:\n",
    "            mt_d = data_to_strm(d)\n",
    "            strm.seq_spc(mt_d)\n",
    "        else:\n",
    "            mt_d = data_to_strm(d)\n",
    "            strm.meta_event(mt_typ, mt_d)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "def b_read(v):\n",
    "    return unpack('>%s' % {1:'B', 2:'H', 4:'L'}[len(v)], v)[0]\n",
    "def op_bew_wrt(v, l):\n",
    "    return pack('>%s' % {1:'B', 2:'H', 4:'L'}[l], v)\n",
    "def op_rd_var(v):\n",
    "    count = 0\n",
    "    for b in unpack('%sB' % len(v), v):\n",
    "        count = (count << 7) + (b & 0x7F)\n",
    "        if not 0x80 & b: \n",
    "            break\n",
    "    return count\n",
    "def get_len_var(v):\n",
    "    if v <= 127:\n",
    "        return 1\n",
    "    elif v <= 16383:\n",
    "        return 2\n",
    "    elif v <= 2097151:\n",
    "        return 3\n",
    "    else:\n",
    "        return 4\n",
    "def op_wrt_var(v):\n",
    "    s = data_to_bits(v, get_len_var(v))\n",
    "    for i in range(len(s)-1):\n",
    "        s[i] = s[i] | 0x80\n",
    "    return strm_to_data(s)\n",
    "def data_to_bits(v, l=1, nbs=7):\n",
    "    b = [(v >> (j*nbs)) & 0x7F for j in range(l)]\n",
    "    b.reverse()\n",
    "    return b\n",
    "def data_to_strm(v):\n",
    "    return unpack('%sB' % len(v), v)\n",
    "def strm_to_data(v):\n",
    "    if not v: return ''\n",
    "    return pack('%sB' % len(v), *v)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MidiDataRead(MOStrm):\n",
    "  def __init__(self, fname, rng=(21, 109), dtm=0.2):\n",
    "    self.nts = []\n",
    "    self._tmp = 500000\n",
    "    self.bt = 0\n",
    "    self.t = 0.0\n",
    "    m_in = MIFl(self, fname)\n",
    "    m_in.op_rd()\n",
    "    self.nts = [n for n in self.nts if n[2] is not None]  \n",
    "    ln = int(np.ceil(max(list(zip(*self.nts))[2]) / dtm))  \n",
    "    self.pio_rl = np.zeros((ln, rng[1]-rng[0]))\n",
    "    for n in self.nts:\n",
    "      self.pio_rl[int(np.ceil(n[1]/dtm)) : int(np.ceil(n[2]/dtm)), n[0]-rng[0]] = 1\n",
    "  def t_in_sec(self):\n",
    "    return self.t + self._tmp * (self.t_abs() - self.bt) * 1e-6 / self.div\n",
    "  def tmp(self, v):\n",
    "    self.t = self.t_in_sec()\n",
    "    self.bt = self.t_abs()\n",
    "    self._tmp = v  \n",
    "  def hdr(self, fmt=0, num_tr=1, divs=96):\n",
    "    self.div = divs\n",
    "  def midi_nt_on(self, chnl=0, nt=0x40, vel=0x40):\n",
    "    self.nts.append([nt, self.t_in_sec(), None])\n",
    "  def midi_nt_off(self, chnl=0, nt=0x40, vel=0x40):\n",
    "    i = len(self.nts) - 1\n",
    "    while i >= 0 and self.nts[i][0] != nt:\n",
    "      i -= 1\n",
    "    if i >= 0 and self.nts[i][2] is None:\n",
    "      self.nts[i][2] = self.t_in_sec()\n",
    "  def ssx_ev(*args):\n",
    "    pass\n",
    "  def dvc_nam(*args):\n",
    "    pass\n",
    "def midiwrite(fname, pio_rl, rng=(21, 109), dtm=0.2, patch=0):\n",
    "  md = MOFl(fname)\n",
    "  md.hdr(divs=100)\n",
    "  md.s_o_trk() \n",
    "  md.pch_chg(chnl=0, pch=patch)\n",
    "  tm = 0\n",
    "  smp = [i.nonzero()[0] + rng[0] for i in pio_rl]\n",
    "  for i in range(len(smp)):\n",
    "    for f in smp[i]:\n",
    "      if i==0 or f not in smp[i-1]:\n",
    "        md.time_update(tm)\n",
    "        md.midi_nt_on(chnl=0, nt=f, vel=90)\n",
    "        tm = 0\n",
    "    tm += int(dtm*200)\n",
    "    for f in smp[i]:\n",
    "      if i==len(smp)-1 or f not in smp[i+1]:\n",
    "        md.time_update(tm)\n",
    "        md.midi_nt_off(chnl=0, nt=f, vel=0)\n",
    "        tm = 0\n",
    "  md.time_update(0)\n",
    "  md.e_o_trk()\n",
    "  md.end_of_file()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# PyTorch CODE STARTS HERE !"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dataset Instructions\n",
    "- For this exercise, we will be using a set of Mozart’s compositions. \n",
    "- We download the dataset from here: http://www.piano-midi.de/zip/mozart.zip . \n",
    "- The downloaded folder consists of 21 MIDI files which we shall split into 18 training and 3 validation set files (alphabetically).  \n",
    "- We create two subfolders within the mozart folder - train and valid where we move the 18 training and 3 validation files respectively\n",
    "- The downloaded data should be stored under ./mozart/train and ./mozart/valid"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-27T12:20:52.867674Z",
     "start_time": "2018-11-27T12:20:52.819795Z"
    }
   },
   "outputs": [],
   "source": [
    "def md_fl_to_pio_rl(md_fl):    \n",
    "    md_d = MidiDataRead(md_fl, dtm=0.3)\n",
    "    pio_rl = md_d.pio_rl.transpose()\n",
    "    pio_rl[pio_rl > 0] = 1    \n",
    "    return pio_rl\n",
    "def pd_pio_rl(pio_rl, mx_l=132333, pd_v=0):        \n",
    "    orig_rol_len = pio_rl.shape[1]    \n",
    "    pdd_rol = np.zeros((88, mx_l))\n",
    "    pdd_rol[:] = pd_v    \n",
    "    pdd_rol[:, - orig_rol_len:] = pio_rl\n",
    "    return pdd_rol\n",
    "class NtGenDataset(data.Dataset):    \n",
    "    def __init__(self, md_pth, mx_seq_ln=1491):        \n",
    "        self.md_pth = md_pth        \n",
    "        md_fnames = os.listdir(md_pth)        \n",
    "        self.mx_seq_ln = mx_seq_ln        \n",
    "        md_fnames_ful = map(lambda fname: os.path.join(md_pth, fname),md_fnames)        \n",
    "        self.md_fnames_ful = list(md_fnames_ful)        \n",
    "        if mx_seq_ln is None:            \n",
    "            self.mx_len_upd()    \n",
    "    def mx_len_upd(self):        \n",
    "        seq_lens = map(lambda fname: md_fl_to_pio_rl(fname).shape[1],self.md_fnames_ful)        \n",
    "        mx_l = max(seq_lens)\n",
    "        self.mx_seq_ln = mx_l    \n",
    "    def __len__(self):        \n",
    "        return len(self.md_fnames_ful)    \n",
    "    def __getitem__(self, index):        \n",
    "        md_fname_ful = self.md_fnames_ful[index]        \n",
    "        pio_rl = md_fl_to_pio_rl(md_fname_ful)\n",
    "        seq_len = pio_rl.shape[1] - 1\n",
    "        ip_seq = pio_rl[:, :-1]\n",
    "        gt_seq = pio_rl[:, 1:]\n",
    "        ip_seq_pad = pd_pio_rl(ip_seq, mx_l=self.mx_seq_ln)\n",
    "        gt_seq_pad = pd_pio_rl(gt_seq,mx_l=self.mx_seq_ln,pd_v=-100)       \n",
    "        ip_seq_pad = ip_seq_pad.transpose()\n",
    "        gt_seq_pad = gt_seq_pad.transpose()\n",
    "        return (torch.FloatTensor(ip_seq_pad),\n",
    "                torch.LongTensor(gt_seq_pad), torch.LongTensor([seq_len]))\n",
    "def pos_proc_seq(btch):\n",
    "    ip_seqs, op_seqs, lens = btch    \n",
    "    ip_seq_splt_btch = ip_seqs.split(split_size=1)\n",
    "    op_seq_splt_btch = op_seqs.split(split_size=1)\n",
    "    btch_splt_lens = lens.split(split_size=1)\n",
    "    tr_data_tups = zip(ip_seq_splt_btch,\n",
    "                               op_seq_splt_btch,\n",
    "                               btch_splt_lens)\n",
    "    ord_tr_data_tups = sorted(tr_data_tups,\n",
    "                                         key=lambda c: int(c[2]),\n",
    "                                         reverse=True)\n",
    "    ip_seq_splt_btch, op_seq_splt_btch, btch_splt_lens = zip(*ord_tr_data_tups)\n",
    "    ord_ip_seq_btch = torch.cat(ip_seq_splt_btch)\n",
    "    ord_op_seq_btch = torch.cat(op_seq_splt_btch)\n",
    "    ord_btch_lens = torch.cat(btch_splt_lens)    \n",
    "    ord_ip_seq_btch = ord_ip_seq_btch[:, -ord_btch_lens[0, 0]:, :]\n",
    "    ord_op_seq_btch = ord_op_seq_btch[:, -ord_btch_lens[0, 0]:, :]    \n",
    "    tps_ip_seq_btch = ord_ip_seq_btch.transpose(0, 1)    \n",
    "    ord_btch_lens_l = list(ord_btch_lens)\n",
    "    ord_btch_lens_l = map(lambda k: int(k), ord_btch_lens_l)    \n",
    "    return tps_ip_seq_btch, ord_op_seq_btch, list(ord_btch_lens_l)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-27T12:21:07.678124Z",
     "start_time": "2018-11-27T12:20:56.931002Z"
    },
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "training_dataset = NtGenDataset('./mozart/train', mx_seq_ln=None)\n",
    "training_datasetloader = data.DataLoader(training_dataset, batch_size=5,shuffle=True, drop_last=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-27T12:21:08.232332Z",
     "start_time": "2018-11-27T12:21:07.693745Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([5, 4812, 88])"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_train = next(iter(training_datasetloader))\n",
    "X_train[0].shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-27T12:21:11.258476Z",
     "start_time": "2018-11-27T12:21:08.259258Z"
    }
   },
   "outputs": [],
   "source": [
    "validation_dataset = NtGenDataset('./mozart/valid/', mx_seq_ln=None)\n",
    "validation_datasetloader = data.DataLoader(validation_dataset, batch_size=3, shuffle=False, drop_last=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-27T12:21:11.406325Z",
     "start_time": "2018-11-27T12:21:11.278687Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([3, 1587, 88])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "X_validation = next(iter(validation_datasetloader))\n",
    "X_validation[0].shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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3347br31VmRmZqKyshKjRo3C8ePHcfvttyMlJQW//PILZs+ejZMnT9Z7X+S9995DWVkZZs6cierqarzyyiu48MILsX37dsTFxTmXczgcmDBhAi644AK8+OKLCAoKAgDcfvvtzu5/9957L7Kzs/H6669jy5Yt+Pnnn6HX65Gfn4/x48cjJiYGDz/8MMLDw5GTk4MvvvjCLS/ebKvWgQMHcNVVV+Hmm2/G9OnT8e9//xs33ngjBg0ahN69e2PkyJG499578eqrr+KRRx5Bz549AcD53wULFiA4OBizZs1CcHAwVq5ciccffxylpaV44YUXANS8F1JSUoJjx47hH//4B4CabogNWb58OSZOnIiuXbviiSeeQFVVFV577TUMHz4cmzdvdrsIB4BrrrkG6enpmDdvHjZv3oz/+7//Q2xsLJ577rlGj3vtd3Tuuedi3rx5yMvLwyuvvIKff/4ZW7ZsQXh4OB599FFkZmbinXfewZNPPon09PR69bSujRs34pdffsHUqVPRpUsX5OTk4K233sLo0aOxa9cu5/FuyvXXX4/77rsP5eXlCA4OhsPhwKeffopZs2ahurq6WWXp2bMn3n//fbf1iouLMWvWLMTGxrptq6ljWquwsBATJ07E1KlTccMNNyAuLg6jR4/GPffcg+DgYDz66KMA4HYOeHLzzTdjwYIFmDhxIm655RY4HA789NNPWLduHQYPHgwAuOWWW7Bw4UJcddVVePDBB7F+/XrMmzcPu3fvxpdffum2vQMHDuD666/H7bffjhtuuAEvvvgiLrvsMrz99tt45JFHcNdddwEA5s2bh2uuuQZ79+6FLLvuu6qqiosvvhhDhw7F888/jyVLlmDOnDlwOBx48sknAdQ83f3DH/6AH374ATfffDMGDBiApUuX4i9/+QuOHz/urOs7d+7EpZdein79+uHJJ5+E0WjEgQMH8PPPPzf4fdx+++04ceIEli1bVu+YebJz506MGDECoaGheOihh6DX6/HPf/4To0ePxurVqzFkyBC35e+55x5ERERgzpw5yMnJwcsvv4y7774bn3zySZP7IqIzCCLq8ObPny8AiI0bN4rXX39dhISEiMrKSiGEEFdffbUYM2aMEEKI1NRUMWnSJLd1a5erZbPZRJ8+fcSFF17olm6xWMT06dPr7XvOnDkCgLjuuusa/KyukydPisjISHHRRRcJq9UqBg4cKFJSUkRJSUmT5UxNTRUAxJIlS9zSn3rqKWGxWMS+ffvc0h9++GGhKIo4cuSIEEKI7OxsAUCYzWZx7Ngx53Lr168XAMQDDzzgTJs+fboAIB5++GG3bf70008CgPjggw/c0pcsWeKW/uWXXzqPSUO83Vbdsv/444/OtPz8fGE0GsWDDz7oTPv0008FAPHDDz/U29+Zx1oIIW6//XYRFBQkqqurnWmTJk0Sqamp9Zat/f7mz5/vTBswYICIjY0VhYWFzrTffvtNyLIs/vSnPznTauvCTTfd5LbNK664QkRFRdXbV102m03ExsaKPn36iKqqKmf64sWLBQDx+OOPO9PqngtN8fR9rF27VgAQ7733XpPrAxAzZ84Up0+fFgaDQbz//vtCCCG++eYbIUmSyMnJcZa7oKDA57LUpWmauPTSS0VwcLDYuXNno2XwdExHjRolAIi333673vK9e/cWo0aNarK8QgixcuVKAUDce++9HvMohBBbt24VAMQtt9zi9vmf//xnAUCsXLnSmVZbr3/55Rdn2tKlS53n6eHDh53p//znP+vV7drz9J577nHLx6RJk4TBYHB+71999ZUAIJ5++mm3PF111VVCkiRx4MABIYQQ//jHP9yOlyeezoOZM2fWa+tqARBz5sxx/vvyyy8XBoNBHDx40Jl24sQJERISIkaOHOlMq63L48aNc363QgjxwAMPCEVRRHFxcYN5JCLP2BWNKMBcc801qKqqwuLFi1FWVobFixc32A0NAMxms/P/i4qKUFJSghEjRmDz5s0+7feOO+7warn4+Hi88cYbWLZsGUaMGIGtW7fi3//+N0JDQ71aPz09HRMmTHBL+/TTTzFixAhERETg1KlTzr9x48ZBVVX8+OOPbstffvnlSEpKcv77vPPOw5AhQzx2hbvzzjvr7SssLAwXXXSR274GDRqE4OBg/PDDDwBc73gsXrwYdrvdY1m83VatXr16YcSIEc5/x8TEIDMzE4cOHWriW6tR91iXlZXh1KlTGDFiBCorK7Fnzx6vtlHXyZMnsXXrVtx4442IjIx0pvfr1w8XXXSRx+/zzHoyYsQIFBYWorS0tMH9/Prrr8jPz8ddd93l9s7WpEmTkJWVhW+++cbnvAPu34fdbkdhYSG6deuG8PBwn+p/REQELr74Ynz00UcAat5pO//885GamtpqZXnqqaewePFiLFiwAL169fJYhqaOqdFoxIwZM7wulyeff/45JEnCnDlz6n1W2/2q9rjPmjXL7fMHH3wQAOqVsVevXhg2bJjz37VPLC688EKkpKTUS/dU3++++263fNx9992w2WxYvny5M0+KouDee++tlychhPMdqdrzdtGiRW7dWFuLqqr4/vvvcfnll6Nr167O9ISEBFx//fVYs2ZNvXPhtttuc+vaNmLECKiqisOHD7d6/og6OwY2RAEmJiYG48aNw4cffogvvvgCqqriqquuanD5xYsXY+jQoTCZTIiMjERMTAzeeustlJSU+LTf9PR0r5edOnUqJk2ahA0bNuDWW2/F2LFjW7Sf/fv3Y8mSJYiJiXH7qx0Vq/aF8lrdu3evt40ePXrUe7dCp9OhS5cu9fZVUlKC2NjYevsrLy937mvUqFGYMmUK5s6di+joaEyePBnz5893e8fA223VqnuRVysiIqLe+zgN2blzJ6644gqEhYUhNDQUMTExuOGGGwDA5+MNwHlhlZmZWe+znj174tSpU/UGXDizDBEREQDQaBka209WVlazL/Cqqqrw+OOPO9+5iI6ORkxMDIqLi33+Pq6//nosW7YMR44cwVdffdXgzYTmlGXJkiWYO3cuZs+ejSlTprh95ssxTUpKqjdym68OHjyIxMREt0D2TIcPH4Ysy+jWrZtbenx8PMLDw+uV8cw6ERYWBgBITk72mH5mXZFl2S1IAGrOZwDOc/rw4cNITExESEiI23K13TRr83Tttddi+PDhuOWWWxAXF4epU6fiv//9b6sFOQUFBaisrGzwnNE0DUePHnVLb845Q0Se8R0bogB0/fXX49Zbb0Vubi4mTpzY4AhRP/30E/7whz9g5MiRePPNN5GQkAC9Xo/58+fXexm6KXXvHDelsLDQ+aLxrl27oGmaW595X/ejaRouuugiPPTQQx7Xqb3I8ZXRaKyXL03TEBsbiw8++MDjOjExMQBq7hp/9tlnWLduHb7++mssXboUN910E/7+979j3bp1CA4O9npbtRoahUl4MSp/cXExRo0ahdDQUDz55JPIyMiAyWTC5s2b8de//rVN7k570pIytLZ77rkH8+fPx/33349hw4YhLCwMkiRh6tSpPn8ff/jDH2A0GjF9+nRYrVZcc801rZLH7OxsTJs2DRdddBGefvppt898Paa+nKOtwdvJeRuqE/6oK2azGT/++CN++OEHfPPNN1iyZAk++eQTXHjhhfj+++/9MiJgRzpniAIdAxuiAHTFFVfg9ttvx7p16xp9wfTzzz+HyWTC0qVLYTQanenz58+vt6y3FynemDlzJsrKyjBv3jzMnj0bL7/8cr1uK77IyMhAeXm51/OW7N+/v17avn376r3o3tC+li9fjuHDh3t1oTh06FAMHToUf/vb3/Dhhx9i2rRp+Pjjj3HLLbf4vC1vNHScVq1ahcLCQnzxxRcYOXKkMz07O9vrbZyptqvV3r176322Z88eREdHt8rkmHX3c+GFF7p9tnfvXo9dvrzx2WefYfr06fj73//uTKuurkZxcbHP2zKbzbj88svxn//8BxMnTmxwzhpfylJVVYUrr7wS4eHh+Oijj+oF2b4c08b4cm5nZGRg6dKlOH36dINPbVJTU6FpGvbv3+98IgIAeXl5KC4ubvbxaoimaTh06JDbDYx9+/YBgPOcTk1NxfLly1FWVub21Ka2u17dPMmyjLFjx2Ls2LF46aWX8Mwzz+DRRx/FDz/80GAb4+13GBMTg6CgoAbPGVmW6z2pIqLWw65oRAEoODgYb731Fp544glcdtllDS6nKAokSXIbGjYnJwdfffVVvWUtFkuzLvjO9Nlnn+GTTz7Bs88+i4cffhhTp07F//t//895IdIc11xzDdauXYulS5fW+6y4uLjevD5fffWV29C6GzZswPr1650jyTW1L1VV8dRTT9X7zOFwOL+joqKiendUBwwYAADO7mjebssXtYHEmevW3vWtmyebzVZvMtfabXjTFSshIQEDBgzAwoUL3fa3Y8cOfP/9916NcueNwYMHIzY2Fm+//bZbV77vvvsOu3fvbvZcMYqi1DtGr732Wr2hkr315z//GXPmzMFjjz3W4DK+lOWOO+7Avn378OWXXzq7H52Zf8C7Y9oYX87tKVOmQAiBuXPn1vusNh+1x/3M0QhfeuklAGiTuX1ef/11t3y8/vrr0Ov1zm6ul1xyCVRVdVsOAP7xj39AkiTnuX/69Ol62z7zvPWkofPuTIqiYPz48Vi0aJFb19e8vDx8+OGHuOCCC7x+35CIfMcnNkQBqnY+j8ZMmjQJL730Ei6++GJcf/31yM/PxxtvvIFu3bph27ZtbssOGjQIy5cvx0svvYTExESkp6fXG5a0Kfn5+bjzzjsxZswY58u+r7/+On744QfceOONWLNmjddd0ur6y1/+gv/973+49NJLncMfV1RUYPv27fjss8+Qk5Pjdge9W7duuOCCC3DnnXfCarXi5ZdfRlRUVINd2eoaNWoUbr/9dsybNw9bt27F+PHjodfrsX//fnz66ad45ZVXcNVVV2HhwoV48803ccUVVyAjIwNlZWV49913ERoa6rzw83ZbvhgwYAAURcFzzz2HkpISGI1GXHjhhTj//PMRERGB6dOn495774UkSXj//fc9dmcZNGgQPvnkE8yaNQvnnnsugoODGwyQX3jhBUycOBHDhg3DzTff7BzuOSwsrN7cHc2l1+vx3HPPYcaMGRg1ahSuu+465xDJaWlpeOCBB5q13UsvvRTvv/8+wsLC0KtXL6xduxbLly9HVFRUs7bXv39/9O/fv9FlvC3LN998g/feew9TpkzBtm3b3M7H4OBgXH755T4d08YMGjQIb731Fp5++ml069YNsbGx9Z4m1RozZgz++Mc/4tVXX8X+/ftx8cUXQ9M0/PTTT87zun///pg+fTreeecdZ3e5DRs2YOHChbj88ssxZswYn/LXFJPJhCVLlmD69OkYMmQIvvvuO3zzzTd45JFHnN05L7vsMowZMwaPPvoocnJy0L9/f3z//fdYtGgR7r//fuew4E8++SR+/PFHTJo0CampqcjPz8ebb76JLl26OOfnaug7BIB7770XEyZMgKIomDp1qsdln376aedcOXfddRd0Oh3++c9/wmq14vnnn2/V74aIzuCPodiIyDfeDnHrabjnf/3rX6J79+7CaDSKrKwsMX/+fI/DNO/Zs0eMHDlSmM1mAcA59POZw9nWdeZ2rrzyShESEiJycnLcllu0aJEAIJ577jmf81+rrKxMzJ49W3Tr1k0YDAYRHR0tzj//fPHiiy8Km80mhHAN0/rCCy+Iv//97yI5OVkYjUYxYsQI8dtvv7ltb/r06cJisTSYl3feeUcMGjRImM1mERISIvr27SseeughceLECSGEEJs3bxbXXXedSElJEUajUcTGxopLL71U/Prrrz5vq7Gyjxo1qt5Qve+++67o2rWrUBTFbXjcn3/+WQwdOlSYzWaRmJgoHnroIefQunWH0C0vLxfXX3+9CA8PFwCcQz97GuZWCCGWL18uhg8fLsxmswgNDRWXXXaZ2LVrl9syDdWT2rqbnZ3d4Hdd65NPPhEDBw4URqNRREZGimnTprkN2113e94M91xUVCRmzJghoqOjRXBwsJgwYYLYs2ePSE1N9Ti0+Znw+3DPjWmo3E2VpbYcnv7qDsXt7TEdNWqU6N27t8c85ubmikmTJomQkBABoMmhnx0Oh3jhhRdEVlaWMBgMIiYmRkycOFFs2rTJuYzdbhdz584V6enpQq/Xi+TkZDF79my3IaiFaLhee/pu656/tWrP04MHD4rx48eLoKAgERcXJ+bMmSNUVXVbv6ysTDzwwAMiMTFR6PV60b17d/HCCy+4DaW8YsUKMXnyZJGYmCgMBoNITEwU1113ndtQ8p7OA4fDIe655x4RExMjJElya/dwxnDPQtS0DxMmTBDBwcEiKChIjBkzxm3IayEarss//PBDg0O6E1HjJCH4dhoRdQ45OTlIT0/HCy+8gD//+c/+zg4RtdCNN96Izz77DOXl5f7OChEFAL5jQ0REREREAY+BDRERERERBTwGNkREREREFPD4jg0REREREQW8Nnti88YbbyAtLQ0mkwlDhgzBhg0b2mpXRERERER0lmuTwKZ2foQ5c+Zg8+bN6N+/PyZMmID8/Py22B0REREREZ3l2qQr2pAhQ3Duuec6ZwDWNA3Jycm455578PDDDze6rqZpOHHiBEJCQiBJUmtnjYiIiIiIAoQQAmVlZUhMTGxykm9da+/cZrNh06ZNmD17tjNNlmWMGzcOa9eurbe81WqF1Wp1/vv48ePo1atXa2eLiIiIiIgC1NGjR9GlS5dGl2n1wObUqVNQVRVxcXFu6XFxcdizZ0+95efNm4e5c+fWS78Al0AHfWtnj4iIiIiIAoQDdqzBtwgJCWly2VYPbHw1e/ZszJo1y/nv0tJSJCcnQwc9dBIDGyIiIiKis9bvL81484pKqwc20dHRUBQFeXl5bul5eXmIj4+vt7zRaITRaGztbBARERER0Vmk1UdFMxgMGDRoEFasWOFM0zQNK1aswLBhw1p7d0RERERERG3TFW3WrFmYPn06Bg8ejPPOOw8vv/wyKioqMGPGjLbYHRERERERneXaJLC59tprUVBQgMcffxy5ubkYMGAAlixZUm9AASIiIiIiotbQJvPYtERpaSnCwsIwGpM5eAARERER0VnMIexYhUUoKSlBaGhoo8u2+js2RERERERE7Y2BDRERERERBTwGNkREREREFPAY2BARERERUcBjYENERERERAGPgQ0REREREQU8BjZERERERBTwGNgQEREREVHAY2BDREREREQBj4ENEREREREFPAY2REREREQU8BjYEBERERFRwGNgQ0REREREAY+BDRERERERBTwGNkREREREFPAY2BARERERUcBjYENERERERAGPgQ0REREREQU8BjZERERERBTwGNgQEREREVHAY2BDREREREQBj4ENEREREREFPAY2REREREQU8BjYEBERERFRwGNgQ0REREREAY+BDRERERERBTwGNkREREREFPAY2BARERERUcBjYENERERERAGPgQ0REREREQU8BjZERERERBTwGNgQEREREVHAY2BDREREREQBj4ENEREREREFPAY2REREREQU8BjYEBERERFRwNP5OwNE1EokCZC8vFehqW2bF6LW4G2dFhogRNvnh4iIOvT1BgMbok5CDOuHkxdYoBobX86cJxC/9Bgch4+2T8aImkHS6WAdOwD5gwwQSuPLhmZriFyyH+qpwvbJHBHR2UqSoI4aiNyhJmj6xhe1HBOIXZINx8nc9skbGNgQdQ6ShNyhFjx2ywfoazjZ6KJzjl2G0/tSoTCwoQ5MMhpxbIweC655HZFydaPLXv/bTcCWaICBDRFRm5J0epwYYcJL0/+FVF1Ro8vetf86qDtiAAY2ROQr2Q5kW2NhkuyNLldQFQydQ2unXBE1kxBQbMBBWywKlcpGF62sNgBaVTtljIjo7CbbgRxbNGxNPE4vqTIhVmvf6w0GNkSdgRBIWH0an5eOg2ZofFHTaQ1BB3LgaJ+cETWLVm1F6rcVePXw1U0Oc5Nw3AGcPNw+GSMiOosJhx1dlpXgX3l/gGgiigjOVyEfPoj2fMtGEsL7Ny7nzZuHL774Anv27IHZbMb555+P5557DpmZmc5lqqur8eCDD+Ljjz+G1WrFhAkT8OabbyIuLs6rfZSWliIsLAyjMRk6qYnOe0RERERE1Gk5hB2rsAglJSUIDQ1tdFmfhntevXo1Zs6ciXXr1mHZsmWw2+0YP348KioqnMs88MAD+Prrr/Hpp59i9erVOHHiBK688srmlYSIiIiIiMgLPj2xOVNBQQFiY2OxevVqjBw5EiUlJYiJicGHH36Iq666CgCwZ88e9OzZE2vXrsXQoUOb3Caf2BAREREREdCG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j/Rj+WflMM7eYmBDRERERNSByL/uRvfdZkCqM86XqkItr/BfpgIAAxsiIiIiog5E2G1Qi23+zkbA4XDPREREREQU8BjYEBERERFRwGNgQ0REREREAY+BDRERERERBTwGNkREREREFPAY2BARERERUcBjYENERERERAGPgQ0REREREQU8BjZERERERBTwGNgQEREREVHAY2BDREREREQBj4ENEREREREFPAY2REREREQU8BjYEBERERFRwGNgQ0REREREAY+BDRERERERBTwGNkREREREFPAY2BARERERUcBjYENERERERAGPgQ0REREREQU8BjZERERERBTwGNgQEREREVHAY2BDREREREQBj4ENEREREREFPAY2REREREQU8BjYEBERERFRwGNgQ0REREREAY+BDRERERERBTwGNkREREREFPB8CmxUVcVjjz2G9PR0mM1mZGRk4KmnnoIQwrmMEAKPP/44EhISYDabMW7cOOzfv7/VM05ERERERFTLp8Dmueeew1tvvYXXX38du3fvxnPPPYfnn38er732mnOZ559/Hq+++irefvttrF+/HhaLBRMmTEB1dXWrZ56IiIiIiAgAdL4s/Msvv2Dy5MmYNGkSACAtLQ0fffQRNmzYAKDmac3LL7+M//f//h8mT54MAHjvvfcQFxeHr776ClOnTm3l7BMREREREfn4xOb888/HihUrsG/fPgDAb7/9hjVr1mDixIkAgOzsbOTm5mLcuHHOdcLCwjBkyBCsXbvW4zatVitKS0vd/oiIiIiIiHzh0xObhx9+GKWlpcjKyoKiKFBVFX/7298wbdo0AEBubi4AIC4uzm29uLg452dnmjdvHubOnducvBMREREREQHw8YnNf//7X3zwwQf48MMPsXnzZixcuBAvvvgiFi5c2OwMzJ49GyUlJc6/o0ePNntbRERERER0dvLpic1f/vIXPPzww853Zfr27YvDhw9j3rx5mD59OuLj4wEAeXl5SEhIcK6Xl5eHAQMGeNym0WiE0WhsZvaJiIiIiIh8fGJTWVkJWXZfRVEUaJoGAEhPT0d8fDxWrFjh/Ly0tBTr16/HsGHDWiG7RERERERE9fn0xOayyy7D3/72N6SkpKB3797YsmULXnrpJdx0000AAEmScP/99+Ppp59G9+7dkZ6ejsceewyJiYm4/PLL2yL/REREREREvgU2r732Gh577DHcddddyM/PR2JiIm6//XY8/vjjzmUeeughVFRU4LbbbkNxcTEuuOACLFmyBCaTqdUzT0REREREBACSEEL4OxN1lZaWIiwsDKMxGTpJ7+/sEBERERGRnziEHauwCCUlJQgNDW10WZ/esSEiIiIiIuqIGNgQEREREVHAY2BDREREREQBj4ENEREREREFPAY2REREREQU8BjYEBERERFRwGNgQ0REREREAY+BDRERERERBTwGNkREREREFPAY2BARERERUcBjYENERERERAFP5+8MnEkIAQBwwA4IP2eGiIiIiIj8xgE7AFeM0JgOF9iUlZUBANbgWz/nhIiIiIiIOoKysjKEhYU1uowkvAl/2pGmaThx4gSEEEhJScHRo0cRGhrq72yRH5SWliI5OZl14CzGOkCsA8Q6QKwDZzchBMrKypCYmAhZbvwtmg73xEaWZXTp0gWlpaUAgNDQUFbisxzrALEOEOsAsQ4Q68DZq6knNbU4eAAREREREQU8BjZERERERBTwOmxgYzQaMWfOHBiNRn9nhfyEdYBYB4h1gFgHiHWAvNXhBg8gIiIiIiLyVYd9YkNEREREROQtBjZERERERBTwGNgQEREREVHAY2BDREREREQBr0MGNm+88QbS0tJgMpkwZMgQbNiwwd9ZojbyxBNPQJIkt7+srCzn59XV1Zg5cyaioqIQHByMKVOmIC8vz485ppb68ccfcdlllyExMRGSJOGrr75y+1wIgccffxwJCQkwm80YN24c9u/f77bM6dOnMW3aNISGhiI8PBw333wzysvL27EU1FJN1YMbb7yxXttw8cUXuy3DehC45s2bh3PPPRchISGIjY3F5Zdfjr1797ot4037f+TIEUyaNAlBQUGIjY3FX/7yFzgcjvYsCjWTN3Vg9OjR9dqBO+64w20Z1gGqq8MFNp988glmzZqFOXPmYPPmzejfvz8mTJiA/Px8f2eN2kjv3r1x8uRJ59+aNWucnz3wwAP4+uuv8emnn2L16tU4ceIErrzySj/mllqqoqIC/fv3xxtvvOHx8+effx6vvvoq3n77baxfvx4WiwUTJkxAdXW1c5lp06Zh586dWLZsGRYvXowff/wRt912W3sVgVpBU/UAAC6++GK3tuGjjz5y+5z1IHCtXr0aM2fOxLp167Bs2TLY7XaMHz8eFRUVzmWa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",
      "text/plain": [
       "<Figure size 1000x700 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10,7))\n",
    "plt.title(\"Matrix representation of a Mozart composition\")\n",
    "plt.imshow(X_validation[0][0][:300].numpy().T);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Music LSTM"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### model definition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-27T12:22:33.314323Z",
     "start_time": "2018-11-27T12:22:33.291386Z"
    }
   },
   "outputs": [],
   "source": [
    "class MusicLSTM(nn.Module):    \n",
    "    def __init__(self, ip_sz, hd_sz, n_cls, lyrs=2):        \n",
    "        super(MusicLSTM, self).__init__()        \n",
    "        self.ip_sz = ip_sz\n",
    "        self.hd_sz = hd_sz\n",
    "        self.n_cls = n_cls\n",
    "        self.lyrs = lyrs        \n",
    "        self.nts_enc = nn.Linear(in_features=ip_sz, out_features=hd_sz)        \n",
    "        self.bn_layer = nn.BatchNorm1d(hd_sz)        \n",
    "        self.lstm_layer = nn.LSTM(hd_sz, hd_sz, lyrs)        \n",
    "        self.fc_layer = nn.Linear(hd_sz, n_cls)\n",
    "        \n",
    "    def forward(self, ip_seqs, ip_seqs_len, hd=None):\n",
    "        nts_enc = self.nts_enc(ip_seqs)       \n",
    "        nts_enc_rol = nts_enc.permute(1,2,0).contiguous()\n",
    "        nts_enc_nrm = self.bn_layer(nts_enc_rol)        \n",
    "        nts_enc_nrm_drp = nn.Dropout(0.25)(nts_enc_nrm)\n",
    "        nts_enc_ful = nts_enc_nrm_drp.permute(2,0,1)\n",
    "        \n",
    "        pkd = torch.nn.utils.rnn.pack_padded_sequence(nts_enc_ful, ip_seqs_len)\n",
    "        op, hd = self.lstm_layer(pkd, hd)\n",
    "        \n",
    "        op, op_l = torch.nn.utils.rnn.pad_packed_sequence(op)\n",
    "        \n",
    "        op_nrm = self.bn_layer(op.permute(1,2,0).contiguous())\n",
    "        op_nrm_drp = nn.Dropout(0.1)(op_nrm)\n",
    "        lgts = self.fc_layer(op_nrm_drp.permute(2,0,1))\n",
    "        lgts = lgts.transpose(0, 1).contiguous()\n",
    "        \n",
    "        rev_lgts = (1 - lgts)\n",
    "        \n",
    "        zero_one_lgts = torch.stack((lgts, rev_lgts), dim=3).contiguous()\n",
    "        flt_lgts = zero_one_lgts.view(-1, 2)\n",
    "        return flt_lgts, hd"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### training and validation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-26T16:16:18.949227Z",
     "start_time": "2018-11-26T16:16:18.930281Z"
    }
   },
   "outputs": [],
   "source": [
    "def lstm_model_training(lstm_model, lr, ep=10, val_loss_best=float(\"inf\")):\n",
    "    list_of_losses = []\n",
    "    list_of_val_losses =[]\n",
    "    model_params = lstm_model.parameters()\n",
    "    opt = torch.optim.Adam(model_params, lr=lr)\n",
    "    grad_clip = 1.0\n",
    "    for curr_ep in range(ep):\n",
    "        lstm_model.train()\n",
    "        loss_ep = []\n",
    "        for batch in training_datasetloader:\n",
    "            post_proc_b = pos_proc_seq(batch)\n",
    "            ip_seq_b, op_seq_b, seq_l = post_proc_b\n",
    "            op_seq_b_v =  Variable(op_seq_b.contiguous().view(-1).cpu())\n",
    "            ip_seq_b_v = Variable(ip_seq_b.cpu())\n",
    "            opt.zero_grad()\n",
    "            lgts, _ = lstm_model(ip_seq_b_v, seq_l)\n",
    "            loss = loss_func(lgts, op_seq_b_v)\n",
    "            list_of_losses.append(loss.item())\n",
    "            loss_ep.append(loss.item())\n",
    "            loss.backward()\n",
    "            torch.nn.utils.clip_grad_norm_(lstm_model.parameters(), grad_clip)\n",
    "            opt.step()\n",
    "\n",
    "        tr_ep_cur = sum(loss_ep)/len(training_datasetloader)\n",
    "        print(f'ep {curr_ep} , train loss = {tr_ep_cur}')\n",
    "\n",
    "        vl_ep_cur = evaluate_model(lstm_model)\n",
    "        print(f'ep {curr_ep} , val loss = {vl_ep_cur}\\n')\n",
    "\n",
    "        list_of_val_losses.append(vl_ep_cur)\n",
    "\n",
    "        if vl_ep_cur < val_loss_best:\n",
    "            torch.save(lstm_model.state_dict(), 'best_model.pth')\n",
    "            val_loss_best = vl_ep_cur\n",
    "    return val_loss_best, lstm_model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-27T12:22:58.002722Z",
     "start_time": "2018-11-27T12:22:57.987764Z"
    }
   },
   "outputs": [],
   "source": [
    "def evaluate_model(lstm_model):\n",
    "    lstm_model.eval()\n",
    "    vl_loss_full = 0.0\n",
    "    seq_len = 0.0\n",
    "\n",
    "    for batch in validation_datasetloader:\n",
    "        post_proc_b = pos_proc_seq(batch)\n",
    "        ip_seq_b, op_seq_b, seq_l = post_proc_b\n",
    "        op_seq_b_v =  Variable( op_seq_b.contiguous().view(-1).cpu() )\n",
    "        ip_seq_b_v = Variable( ip_seq_b.cpu() )\n",
    "        lgts, _ = lstm_model(ip_seq_b_v, seq_l)\n",
    "        loss = loss_func(lgts, op_seq_b_v)\n",
    "        vl_loss_full += loss.item()\n",
    "        seq_len += sum(seq_l)\n",
    "\n",
    "    return vl_loss_full/(seq_len*88)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-26T16:19:05.465667Z",
     "start_time": "2018-11-26T16:16:20.312589Z"
    },
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.10/site-packages/transformers/utils/generic.py:441: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
      "  _torch_pytree._register_pytree_node(\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ep 0 , train loss = 1.4977599183718364\n",
      "ep 0 , val loss = 3.359648520086258e-06\n",
      "\n",
      "ep 1 , train loss = 2.2600611249605813\n",
      "ep 1 , val loss = 9.399909746101137e-07\n",
      "\n",
      "ep 2 , train loss = 1.3531951705614726\n",
      "ep 2 , val loss = 1.6474151244813573e-06\n",
      "\n",
      "ep 3 , train loss = 0.8641716837882996\n",
      "ep 3 , val loss = 5.708250628016667e-07\n",
      "\n",
      "ep 4 , train loss = 0.6801068683465322\n",
      "ep 4 , val loss = 2.1908977324171416e-06\n",
      "\n",
      "ep 5 , train loss = 0.6633423070112864\n",
      "ep 5 , val loss = 5.530907588653284e-07\n",
      "\n",
      "ep 6 , train loss = 0.15917229652404785\n",
      "ep 6 , val loss = 5.34580371513167e-07\n",
      "\n",
      "ep 7 , train loss = 0.18867839376131693\n",
      "ep 7 , val loss = 3.671834715649115e-07\n",
      "\n",
      "ep 8 , train loss = 0.1214339683453242\n",
      "ep 8 , val loss = 4.971649697420895e-07\n",
      "\n",
      "ep 9 , train loss = 0.13249559700489044\n",
      "ep 9 , val loss = 4.5515213342500874e-07\n",
      "\n"
     ]
    }
   ],
   "source": [
    "loss_func = nn.CrossEntropyLoss().cpu()\n",
    "lstm_model = MusicLSTM(ip_sz=88, hd_sz=512, n_cls=88).cpu()\n",
    "val_loss_best, lstm_model = lstm_model_training(lstm_model, lr=0.01, ep=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Generate samples"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-26T16:25:11.202962Z",
     "start_time": "2018-11-26T16:25:11.189993Z"
    }
   },
   "outputs": [],
   "source": [
    "def generate_music(lstm_model, ln=100, tmp=1, seq_st=None):\n",
    "    if seq_st is None:\n",
    "        seq_ip_cur = torch.zeros(1, 1, 88)\n",
    "        seq_ip_cur[0, 0, 40] = 1\n",
    "        seq_ip_cur[0, 0, 50] = 0\n",
    "        seq_ip_cur[0, 0, 56] = 0\n",
    "        seq_ip_cur = Variable(seq_ip_cur.cpu())\n",
    "    else:\n",
    "        seq_ip_cur = seq_st\n",
    "        \n",
    "    op_seq = [seq_ip_cur.data.squeeze(1)]\n",
    "    hd = None\n",
    "\n",
    "    for i in range(ln):\n",
    "        op, hd = lstm_model(seq_ip_cur, [1], hd)\n",
    "        probs = nn.functional.softmax(op.div(tmp), dim=1)\n",
    "        seq_ip_cur = torch.multinomial(probs.data, 1).squeeze().unsqueeze(0).unsqueeze(1)\n",
    "        seq_ip_cur = Variable(seq_ip_cur.float())\n",
    "        op_seq.append(seq_ip_cur.data.squeeze(1))\n",
    "\n",
    "    gen_seq = torch.cat(op_seq, dim=0).cpu().numpy()\n",
    "    \n",
    "    return gen_seq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2018-11-26T16:25:36.632928Z",
     "start_time": "2018-11-26T16:25:34.966547Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/opt/conda/lib/python3.10/site-packages/skimage/io/_plugins/matplotlib_plugin.py:150: UserWarning: Low image data range; displaying image with stretched contrast.\n",
      "  lo, hi, cmap = _get_display_range(image)\n"
     ]
    },
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 640x480 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "seq = generate_music(lstm_model, ln=100, tmp=0.8, seq_st=None).transpose()\n",
    "io.imshow(seq)\n",
    "midiwrite('generated_music.mid', seq.transpose(), dtm=0.25)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (Local)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
